package com.yunwaikeji.gy_tool_library.widget;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.text.Editable;
import android.text.Selection;
import android.text.Spannable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.method.ArrowKeyMovementMethod;
import android.text.method.MovementMethod;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
import android.widget.TextView;

import androidx.annotation.Nullable;

import com.yunwaikeji.gy_tool_library.R;
import com.yunwaikeji.gy_tool_library.info.GYEditTextInfo;
import com.yunwaikeji.gy_tool_library.utils.display.GYDisplayUtils;
import com.yunwaikeji.gy_tool_library.utils.GYKeyboardUtils;

import com.yunwaikeji.gy_tool_library.utils.GYTextUtils;
import com.yunwaikeji.gy_tool_library.view.GYTextView;

public class GYEditText extends GYTextView implements GYEditTextInfo {
    public static GYEditText newInstance(Context context) {
        return new GYEditText(context);
    }

    public GYEditText(Context context) {
        super(context);
        init(null);
    }

    public GYEditText(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs, androidx.appcompat.R.attr.editTextStyle);
        init(attrs);
    }

    public GYEditText(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs);
    }

    private Paint mPaint;
    private Paint.FontMetrics fontMetrics;
    private String tipText = "";
    private float tipTextSizePx = GYDisplayUtils.sp2px(getContext(), 14);
    private int tipTextColor = Color.RED;
    private float tipMarginLeftPx = 0;
    private float tipMarginTopPx = 0;
    private float tipMarginRightPx = 0;
    private float tipMarginBottomPx = 0;
    private boolean tipShowLengthTip = false;
    private String tipFilter;
    private int tipLocation = TipLocation.RIGHT_CENTER;//提示信息位置，默认右中
    private boolean search = false;//是否搜索框
    private boolean searchDeleteShow = false;//是否显示搜索框的删除按钮
    private Drawable searchDelete;//删除按钮
    private float searchDeleteWidth = WRAP_CONTENT;//删除按钮宽度
    private float searchDeleteHeight = WRAP_CONTENT;//删除按钮高度
    private float searchDeleteMarginRight = WRAP_CONTENT;//删除按钮距离右边距
    private boolean tipTextBold = false;//提示文字是否粗体

    private int beforeLength = 0;
    private boolean isFocus;//是否获取了焦点
    private boolean edit = true;//是否可以编辑

    private String tipLengthText = "";

    public void init(AttributeSet attributeSet) {
        if (attributeSet != null) {
            TypedArray typedArray = getContext().obtainStyledAttributes(attributeSet, R.styleable.GYEditText);
            tipShowLengthTip = typedArray.getBoolean(R.styleable.GYEditText_tipShowLengthTip, tipShowLengthTip);
            tipText = typedArray.getString(R.styleable.GYEditText_tipText);
            tipTextSizePx = typedArray.getDimension(R.styleable.GYEditText_tipTextSize, tipTextSizePx);
            tipTextColor = typedArray.getColor(R.styleable.GYEditText_tipTextColor, tipTextColor);
            tipMarginLeftPx = typedArray.getDimension(R.styleable.GYEditText_tipMarginLeft, tipMarginLeftPx);
            tipMarginTopPx = typedArray.getDimension(R.styleable.GYEditText_tipMarginTop, tipMarginTopPx);
            tipMarginRightPx = typedArray.getDimension(R.styleable.GYEditText_tipMarginRight, tipMarginRightPx);
            tipMarginBottomPx = typedArray.getDimension(R.styleable.GYEditText_tipMarginBottom, tipMarginBottomPx);
            tipFilter = typedArray.getString(R.styleable.GYEditText_tipFilter);
            tipLocation = typedArray.getInt(R.styleable.GYEditText_tipLocationEdit, tipLocation);
            search = typedArray.getBoolean(R.styleable.GYEditText_search, search);
            searchDeleteShow = typedArray.getBoolean(R.styleable.GYEditText_searchDeleteShow, searchDeleteShow);
            searchDelete = typedArray.getDrawable(R.styleable.GYEditText_searchDelete);
            searchDeleteWidth = typedArray.getDimension(R.styleable.GYEditText_searchDeleteWidth, searchDeleteWidth);
            searchDeleteHeight = typedArray.getDimension(R.styleable.GYEditText_searchDeleteHeight, searchDeleteHeight);
            searchDeleteMarginRight = typedArray.getDimension(R.styleable.GYEditText_searchDeleteMarginRight, searchDeleteMarginRight);
            tipTextBold = typedArray.getBoolean(R.styleable.GYEditText_tipTextBold, tipTextBold);
            getViewSet().setEdit(typedArray.getBoolean(R.styleable.GYEditText_edit, edit));
            typedArray.recycle();
        }
        initTip();
        addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                beforeLength = GYTextUtils.getInstance().getLength(s);
            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {

            }

            @Override
            public void afterTextChanged(Editable s) {
                int nowLength = GYTextUtils.getInstance().getLength(s);
                if (!getViewSet().fitLength()) {
                    String msg = "";
                    if (!getViewSet().getTextString().isEmpty()) {
                        int min = getViewSet().getMinLength();
                        int max = getViewSet().getMaxLength();
                        if (min != MATCH_PARENT && max != MATCH_PARENT) {
                            if (min == max) {
                                msg = min + "位";
                            } else {
                                msg = min + "-" + max + "位";
                            }
                        } else if (min != MATCH_PARENT) {
                            msg = "最少" + min + "位";
                        } else if (max != MATCH_PARENT) {
                            msg = "最多" + max + "位";
                        }
                    }
                    String hint = getViewSet().getHintString();
                    if (!TextUtils.isEmpty(hint) && !TextUtils.isEmpty(tipFilter)) {
                        String[] tipFilters = tipFilter.split("");
                        for (String filter : tipFilters) {
                            if (!TextUtils.isEmpty(filter)) {
                                hint.replace(filter, "");
                            }
                        }
                    }
                    if (beforeLength != nowLength) {
                        tipLengthText = "请输入" + msg + getViewSet().getHintString();
                    }
                } else {
                    tipLengthText = "";
                }
                if (tipShowLengthTip) {
                    getTipBuilder().setTipText(tipLengthText).build();
                }
            }
        });
        if (search) {
            setSingleLine();
            setImeOptions(EditorInfo.IME_ACTION_SEARCH);
        }
        setOnEditorActionListener(new OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
                switch (actionId) {
                    case EditorInfo.IME_ACTION_SEARCH:
                        if (onSearchClickListener != null) {
                            if (!onSearchClickListener.onSearchClick()) {
                                GYKeyboardUtils.getInstance().hideSoftInput(getContext());
                            }
                        }
                        break;
                }
                return false;
            }
        });
        setOnFocusChangeListener((v, hasFocus) -> isFocus = hasFocus);
    }

    private OnSearchClickListener onSearchClickListener;

    public interface OnSearchClickListener {
        boolean onSearchClick();
    }

    private OnTipClickListener onTipClickListener;

    public interface OnTipClickListener {
        void onTipClick(String tip);
    }

    private void initTip() {
        mPaint = new Paint();
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setTextSize(tipTextSizePx);
        mPaint.setTextAlign(Paint.Align.RIGHT);
        fontMetrics = mPaint.getFontMetrics();
        getTipBuilder().setTipText(tipText).build();
    }

    public GYViewSet1 getViewSet() {
        if (gyViewSet1 == null) {
            gyViewSet1 = new GYViewSet1(this);
        }
        return gyViewSet1;
    }

    public TipBuilder getTipBuilder() {
        return new TipBuilder();
    }

    public class TipBuilder {
        public TipBuilder setTipText(String text) {
            tipText = text;
            return this;
        }

        public String getTipTextString() {
            return GYEditText.this.tipText;
        }

        public boolean isTipTextEmpty() {
            return TextUtils.isEmpty(GYEditText.this.tipText);
        }

        public TipBuilder setTipTextSizeSp(float textSizeSp) {
            return setTipTextSizePx(GYDisplayUtils.sp2px(getContext(), textSizeSp));
        }

        public TipBuilder setTipTextSizePx(float textSizePx) {
            tipTextSizePx = textSizePx;
            mPaint.setTextSize(tipTextSizePx);
            return this;
        }

        public TipBuilder setTipMarginRightPx(float tipMarginRightPx) {
            GYEditText.this.tipMarginRightPx = tipMarginRightPx;
            return this;
        }

        public TipBuilder setTipMarginRightDp(float tipMarginRightDp) {
            return setTipMarginRightPx(GYDisplayUtils.dp2px(getContext(), tipMarginRightDp));
        }

        public TipBuilder setTipTextColor(int tipTextColor) {
            GYEditText.this.tipTextColor = tipTextColor;
            mPaint.setColor(tipTextColor);
            return this;
        }

        public TipBuilder setTipLocation(@TipLocation int tipLocation) {
            GYEditText.this.tipLocation = tipLocation;
            return this;
        }

        public GYEditText build() {
            requestLayout();
            invalidate();
            return GYEditText.this;
        }
    }

    public SearchBuilder getSearchBuilder() {
        return new SearchBuilder();
    }

    public class SearchBuilder {
        public SearchBuilder setSearch(boolean search) {
            GYEditText.this.search = search;
            return this;
        }

        public SearchBuilder setSearchDeleteShow(boolean searchDeleteShow) {
            GYEditText.this.searchDeleteShow = searchDeleteShow;
            return this;
        }

        public SearchBuilder setSearchDelete(Drawable searchDelete) {
            GYEditText.this.searchDelete = searchDelete;
            return this;
        }

        public SearchBuilder setSearchDeleteWidthPx(float searchDeleteWidthPx) {
            GYEditText.this.searchDeleteWidth = searchDeleteWidthPx;
            return this;
        }

        public SearchBuilder setSearchDeleteWidthDp(float searchDeleteWidthDp) {
            return setSearchDeleteWidthPx(GYDisplayUtils.dp2px(getContext(), searchDeleteWidthDp));
        }

        public SearchBuilder setSearchDeleteHeightPx(float searchDeleteHeight) {
            GYEditText.this.searchDeleteHeight = searchDeleteHeight;
            return this;
        }

        public SearchBuilder setSearchDeleteHeightDp(float searchDeleteHeightDp) {
            return setSearchDeleteHeightPx(GYDisplayUtils.dp2px(getContext(), searchDeleteHeightDp));
        }

        public SearchBuilder setSearchDeleteMarginRight(float searchDeleteMarginRight) {
            GYEditText.this.searchDeleteMarginRight = searchDeleteMarginRight;
            return this;
        }

        public GYEditText build() {
            requestLayout();
            invalidate();
            return GYEditText.this;
        }
    }

    private GYViewSet1 gyViewSet1;

    public class GYViewSet1 extends GYViewSet {
        public GYViewSet1(View view) {
            super(view);
        }

        public GYEditText toView() {
            return GYEditText.this;
        }

        public GYViewSet1 setTipShowLengthTip(boolean tipShowLengthTip) {
            GYEditText.this.tipShowLengthTip = tipShowLengthTip;
            return this;
        }

        public boolean isFocus() {
            return isFocus;
        }

        public GYViewSet1 setSelection(int index) {
            GYEditText.this.setSelection(index);
            return this;
        }

        public GYViewSet1 setSelection(int start, int stop) {
            GYEditText.this.setSelection(start, stop);
            return this;
        }

        public GYViewSet1 setSelectionEnd() {
            requestFocus();
            String text = getTextString();
            if (!text.isEmpty()) {
                setSelection(text.length());
            }
            return this;
        }

        public GYViewSet1 setEdit(boolean edit) {
            GYEditText.this.edit = edit;
            setFocusable(edit).setFocusableInTouchMode(edit);
            return this;
        }

        public GYViewSet1 setTipFilter(String tipFilter) {
            GYEditText.this.tipFilter = tipFilter;
            return this;
        }

        public GYViewSet1 setOnSearchClickListener(OnSearchClickListener onSearchClickListener) {
            GYEditText.this.onSearchClickListener = onSearchClickListener;
            return this;
        }

        public GYViewSet1 setOnDeleteClickListener(OnDeleteClickListener onDeleteClickListener) {
            GYEditText.this.onDeleteClickListener = onDeleteClickListener;
            return this;
        }

        public GYViewSet1 setOnTipClickListener(OnTipClickListener onTipClickListener) {
            GYEditText.this.onTipClickListener = onTipClickListener;
            return this;
        }
    }

    @Override
    public boolean getFreezesText() {
        return true;
    }

    @Override
    protected boolean getDefaultEditable() {
        return true;
    }

    @Override
    protected MovementMethod getDefaultMovementMethod() {
        return ArrowKeyMovementMethod.getInstance();
    }

    @Override
    public Editable getText() {
        CharSequence text = super.getText();
        // This can only happen during construction.
        if (text == null) {
            return null;
        }
        if (text instanceof Editable) {
            return (Editable) super.getText();
        }
        super.setText(text, BufferType.EDITABLE);
        return (Editable) super.getText();
    }

    @Override
    public void setText(CharSequence text, BufferType type) {
        super.setText(text, BufferType.EDITABLE);
    }

    /**
     * Convenience for {@link Selection#setSelection(Spannable, int, int)}.
     */
    public GYEditText setSelection(int start, int stop) {
        Selection.setSelection(getText(), start, stop);
        return this;
    }

    /**
     * Convenience for {@link Selection#setSelection(Spannable, int)}.
     */
    public GYEditText setSelection(int index) {
        Selection.setSelection(getText(), index);
        return this;
    }

    /**
     * Convenience for {@link Selection#selectAll}.
     */
    public void selectAll() {
        Selection.selectAll(getText());
    }

    /**
     * Convenience for {@link Selection#extendSelection}.
     */
    public void extendSelection(int index) {
        Selection.extendSelection(getText(), index);
    }

    /**
     * Causes words in the text that are longer than the view's width to be ellipsized instead of
     * broken in the middle. {@link TextUtils.TruncateAt#MARQUEE
     * TextUtils.TruncateAt#MARQUEE} is not supported.
     *
     * @param ellipsis Type of ellipsis to be applied.
     * @throws IllegalArgumentException When the value of <code>ellipsis</code> parameter is
     *                                  {@link TextUtils.TruncateAt#MARQUEE}.
     * @see TextView#setEllipsize(TextUtils.TruncateAt)
     */
    @Override
    public void setEllipsize(TextUtils.TruncateAt ellipsis) {
        if (ellipsis == TextUtils.TruncateAt.MARQUEE) {
            throw new IllegalArgumentException("EditText cannot use the ellipsize mode "
                    + "TextUtils.TruncateAt.MARQUEE");
        }
        super.setEllipsize(ellipsis);
    }

    @Override
    public void setInputType(int type) {
        super.setInputType(type);
    }

    @Override
    public CharSequence getAccessibilityClassName() {
        return EditText.class.getName();
    }

    /**
     * @hide
     */
//    @Override
//    protected boolean supportsAutoSizeText() {
//        return false;
//    }
    private float nowSearchDeleteWidth;
    private float nowSearchDeleteHeight;
    private float searchDeleteLeft;
    private float searchDeleteTop;

    private float tipTextWidth;
    private float tipTextHeight;

    @Override
    protected void onDraw(Canvas canvas) {
        //写提示信息
        drawTip(canvas);
        //搜索框删除按钮
        drawSearch(canvas);
        super.onDraw(canvas);
    }

    private float tipTextLeft;
    private float tipTextTop;
    private float tipTextRight;
    private float tipTextBottom;

    //写提示信息
    private void drawTip(Canvas canvas) {
        if (TextUtils.isEmpty(tipText)) {
            canvas.drawColor(Color.TRANSPARENT);
            tipTextWidth = 0;
            tipTextHeight = 0;
        } else {
            mPaint.setColor(tipTextColor);
            int textTypeface = tipTextBold ? Typeface.BOLD : Typeface.NORMAL;
            mPaint.setTypeface(Typeface.create(Typeface.SANS_SERIF, textTypeface));
            float y = -fontMetrics.top;
            float x = 0;
            final float xD = tipMarginLeftPx - tipMarginRightPx;
            final float yD = tipMarginTopPx - tipMarginBottomPx;
            tipTextWidth = mPaint.measureText(tipText);
            tipTextHeight = -fontMetrics.top + fontMetrics.bottom;
            switch (tipLocation) {
                case TipLocation.LEFT_BOTTOM:
                    mPaint.setTextAlign(Paint.Align.LEFT);
                    x = xD;
                    y = y + canvas.getHeight() - tipTextHeight + yD;
                    tipTextLeft = x;
                    break;
                case TipLocation.LEFT_CENTER:
                    mPaint.setTextAlign(Paint.Align.LEFT);
                    x = xD;
                    y = y + canvas.getHeight() / 2 - tipTextHeight / 2 + yD;
                    tipTextLeft = x;
                    break;
                case TipLocation.LEFT_TOP:
                    mPaint.setTextAlign(Paint.Align.LEFT);
                    x = xD;
                    y = y + yD;
                    tipTextLeft = x;
                    break;
                case TipLocation.TOP_CENTER:
                    mPaint.setTextAlign(Paint.Align.CENTER);
                    x = canvas.getWidth() / 2 + xD;
                    y = y + yD;
                    tipTextLeft = x - tipTextWidth / 2;
                    break;
                case TipLocation.TOP_RIGHT:
                    mPaint.setTextAlign(Paint.Align.RIGHT);
                    x = canvas.getWidth() + xD;
                    y = y + yD;
                    tipTextLeft = x - tipTextWidth;
                    break;
                case TipLocation.RIGHT_CENTER:
                    mPaint.setTextAlign(Paint.Align.RIGHT);
                    x = canvas.getWidth() + xD;
                    y = y + canvas.getHeight() / 2 - tipTextHeight / 2 + yD;
                    tipTextLeft = x - tipTextWidth;
                    break;
                case TipLocation.RIGHT_BOTTOM:
                    mPaint.setTextAlign(Paint.Align.RIGHT);
                    x = canvas.getWidth() + xD;
                    y = y + canvas.getHeight() - tipTextHeight + yD;
                    tipTextLeft = x - tipTextWidth;
                    break;
                case TipLocation.BOTTOM_CENTER:
                    mPaint.setTextAlign(Paint.Align.CENTER);
                    x = canvas.getWidth() / 2 + xD;
                    y = y + canvas.getHeight() - tipTextHeight + yD;
                    tipTextLeft = x - tipTextWidth / 2;
                    break;
                case TipLocation.CENTER:
                    mPaint.setTextAlign(Paint.Align.CENTER);
                    x = canvas.getWidth() / 2 + xD;
                    y = y + canvas.getHeight() / 2 - tipTextHeight / 2 + yD;
                    tipTextLeft = x - tipTextWidth / 2;
                    break;
            }
            tipTextTop = y + fontMetrics.top;
            tipTextRight = x + tipTextWidth;
            tipTextBottom = tipTextTop + tipTextHeight;
            canvas.drawText(tipText, x, y, mPaint);
        }
    }

    //搜索框删除按钮
    private void drawSearch(Canvas canvas) {
        if (searchDeleteShow && !getViewSet().getTextString().isEmpty() && searchDelete != null) {
            mPaint.setColor(PAINT_NORMAL_COLOR);
            mPaint.setAntiAlias(true);
            nowSearchDeleteWidth = normalInt(Math.round(searchDeleteWidth), WRAP_CONTENT, normalInt(searchDelete.getIntrinsicWidth(), MATCH_PARENT, GYDisplayUtils.dp2px(getContext(), 18)));
            nowSearchDeleteHeight = normalInt(Math.round(searchDeleteHeight), WRAP_CONTENT, normalInt(searchDelete.getIntrinsicHeight(), MATCH_PARENT, GYDisplayUtils.dp2px(getContext(), 18)));
            Bitmap searchDeleteBitmap = GYDisplayUtils.toBitmap(searchDelete, Math.round(nowSearchDeleteWidth), Math.round(nowSearchDeleteHeight));
            searchDeleteLeft = getWidth() - nowSearchDeleteWidth - normalInt(Math.round(searchDeleteMarginRight), WRAP_CONTENT, 0);
            searchDeleteTop = getHeight() / 2 - nowSearchDeleteHeight / 2;
            canvas.drawBitmap(searchDeleteBitmap, searchDeleteLeft, searchDeleteTop, mPaint);
        }
    }

    private int normalInt(int now, int reference, int to) {
        return now == reference ? to : now;
    }

    private boolean isClickSearchDelete(float x, float y) {
        if (!searchDeleteShow || getViewSet().isTextEmpty()) {
            return false;
        }
        return x >= searchDeleteLeft && x <= searchDeleteLeft + nowSearchDeleteWidth && y >= searchDeleteTop && y <= searchDeleteTop + nowSearchDeleteHeight;
    }

    private boolean isClickTip(float x, float y) {
        if (TextUtils.isEmpty(tipText) || onTipClickListener == null) {
            return false;
        }
        return x >= tipTextLeft && x <= tipTextRight && y >= tipTextTop && y <= tipTextBottom;
    }

    private boolean isClickDelete = false;
    private boolean isClickTip = false;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                isClickDelete = isClickSearchDelete(event.getX(), event.getY());
                if (isClickDelete) {
                    return true;
                }
                isClickTip = isClickTip(event.getX(), event.getY());
                if (isClickTip) {
                    return true;
                }
                break;
            case MotionEvent.ACTION_UP:
                if (isClickSearchDelete(event.getX(), event.getY()) && isClickDelete) {
                    if (onDeleteClickListener == null || !onDeleteClickListener.onDeleteClick()) {
                        getViewSet().setText("");
                    }
                    return true;
                }
                if (isClickTip(event.getX(), event.getY()) && isClickTip) {
                    if (onTipClickListener != null) {
                        onTipClickListener.onTipClick(tipText);
                    }
                    return true;
                }
                break;
        }
        if (edit) {
            return super.onTouchEvent(event);
        } else {
            return false;
        }
    }

    private OnDeleteClickListener onDeleteClickListener;

    public interface OnDeleteClickListener {
        boolean onDeleteClick();
    }
}
